import { reactive } from "vue";
import { storeKey } from "./injectKey";
import { forEachValue } from "./utils";

//! 简易的实现单模块的 vuex 核心基本功能, 多模块则无法使用

export class Store {
  constructor(options) {
    // 将实列存储在 store
    const store = this;
    // opitons 用户选项数据
    store._state = reactive({ data: options.state }); // 将数据变成响应式

    // 数据状态存储在 _state.data 上
    // 获取用户中 getters 计算属性
    const _getters = options.getters;
    // 存放 store 计算属性响应式
    store.getters = {};
    // 遍历 getters 方法
    forEachValue(_getters, (fn, key) => {
      // 设置代理取值 key 值时直接获取函数返回值
      Object.defineProperty(store.getters, key, {
        enumerable: true,
        get: () => fn(store.state), // 将仓库状态传入(注意为什么这里不用 vue3.1 computed 因为在 vuex 中不能用 computed 实现如果组件销毁会移除计算属性，后续会修改)
      });
    });

    // 将用户 mutations，actions 对象中方法分别存放到对应下划线对象中
    store._mutations = Object.create(null); // 创建一个原型(这种原型创建没有其他方法)
    store._actions = Object.create(null); // 异步更新数据方法

    const _mutations = options.mutations;
    const _actions = options.actions;

    // 遍历用户 mutations 对象同步方法
    forEachValue(_mutations, (mutation, key) => {
      // 挂载到对应属性上
      store._mutations[key] = (payload) => {
        // 执行用户写的同步方法
        mutation.call(store, store.state, payload); // 始终将 this 指向 store
      };
    });

    // 遍历用户 actions 对象异步方法
    forEachValue(_actions, (action, key) => {
      store._actions[key] = (payload) => {
        // 传入的参数一是仓库对象可以解构出 commit
        action.call(store, store, payload);
      };
    });
  }

  commit = (type, payload) => {
    this._mutations[type](payload); // 执行对应的方法
  };

  dispatch = (type, payload) => {
    this._actions[type](payload);
  };

  // 访问器属性快速获取状态
  get state() {
    return this._state.data;
  }

  // vue 注册插件
  install(app, injectKey) {
    app.provide(injectKey || storeKey, this); // 暴露一个全局变量，数据试 store 实列数据 (createApp().use(store,"xx"));
    app.config.globalProperties.$store = this; // 类似于 vue 实列上挂载 Vue.prototype.$store = this;
  }
}
